//! Cofactor clearing for G1 and G2.
//!
//! Section 7 of https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-04#page-31
//! the clear_h method here is to multiply the point $P$ with a scaler $h_ell$
//! the $h_ell$ is defined in section 8.7 of the same draft
//! where $h_ell = 0xd201000000010001u64$ for G1 and
//! $h_ell = 0xbc69f08f2ee75b3584c6a0ea91b352888e2a8e9145ad7689986ff0315
//!          08ffe1329c2f178731db956d82bf015d1212b02ec0ec69d7477c1ae954cbc06689
//!          f6a359894c0adebbf6b4e8020005aaa95551,$ for G2
//! this will be faster than multiplying by the co-factor h

use crate::bls12_381::{G1, G2};
use groupy::CurveProjective;

// addchain for 15132376222941642752
// using Bos-Coster (win=2) : 69 links, 2 variables

macro_rules! double {
    ($var:expr, $n:expr) => {
        for _ in 0..$n {
            $var.double();
        }
    };
}

/// Addition chain implementing exponentiation by -z = 0xd201000000010000
/// input a point p, output p^{-z}
pub(super) fn chain_z<PtT: CurveProjective>(var1: &mut PtT, var0: &PtT) {
    *var1 = *var0;
    // 0 : 2
    var1.double();
    // 1 : 3
    var1.add_assign(var0);
    // 2 : 12
    double!(var1, 2);
    // 4 : 13
    var1.add_assign(var0);
    // 5 : 104
    double!(var1, 3);
    // 8 : 105
    var1.add_assign(var0);
    // 9 : 53760
    double!(var1, 9);
    // 18 : 53761
    var1.add_assign(var0);
    // 19 : 230901736800256
    double!(var1, 32);
    // 51 : 230901736800257
    var1.add_assign(var0);
    // 52 : 15132376222941642752
    double!(var1, 16);
}

// chain for 3 * (z^2 - 1) * h2, used to clear cofactor
// compatibly with Budroni-Pintore GLV-based method
#[allow(clippy::cognitive_complexity)]
fn chain_h2_eff<PtT: CurveProjective>(var1: &mut PtT, var0: &PtT) {
    // addchain for
    // 305502333931268344200999753193121504214466019254188142667664032982267604182971884026507427359259977847832272839041616661285803823378372096355777062779109

    // Bos-Coster (win=4) : 604 links, 16 variables
    *var1 = *var0;
    //    0 : 2
    var1.double();
    let mut var4 = *var1;
    //    1 : 3
    var4.add_assign(var0);
    let mut var2 = var4;
    //    2 : 5
    var2.add_assign(var1);
    let mut var3 = var2;
    //    3 : 7
    var3.add_assign(var1);
    let mut var11 = var3;
    //    4 : 9
    var11.add_assign(var1);
    let mut var9 = var11;
    //    5 : 11
    var9.add_assign(var1);
    let mut var10 = var9;
    //    6 : 13
    var10.add_assign(var1);
    let mut var5 = var10;
    //    7 : 15
    var5.add_assign(var1);
    let mut var7 = var5;
    //    8 : 17
    var7.add_assign(var1);
    let mut var15 = var7;
    //    9 : 19
    var15.add_assign(var1);
    let mut var13 = var15;
    //   10 : 21
    var13.add_assign(var1);
    let mut var6 = var13;
    //   11 : 23
    var6.add_assign(var1);
    let mut var14 = var6;
    //   12 : 25
    var14.add_assign(var1);
    let mut var12 = var14;
    //   13 : 27
    var12.add_assign(var1);
    let mut var8 = var12;
    //   14 : 29
    var8.add_assign(var1);
    *var1 = var6;
    //   15 : 46
    var1.double();
    //   16 : 1472
    double!(var1, 5);
    //   21 : 1493
    var1.add_assign(&var13);
    //   22 : 5972
    double!(var1, 2);
    //   24 : 5973
    var1.add_assign(var0);
    //   25 : 3058176
    double!(var1, 9);
    //   34 : 3058205
    var1.add_assign(&var8);
    //   35 : 97862560
    double!(var1, 5);
    //   40 : 97862569
    var1.add_assign(&var11);
    //   41 : 6263204416
    double!(var1, 6);
    //   47 : 6263204437
    var1.add_assign(&var13);
    //   48 : 1603380335872
    double!(var1, 8);
    //   56 : 1603380335877
    var1.add_assign(&var2);
    //   57 : 51308170748064
    double!(var1, 5);
    //   62 : 51308170748071
    var1.add_assign(&var3);
    //   63 : 1641861463938272
    double!(var1, 5);
    //   68 : 1641861463938279
    var1.add_assign(&var3);
    //   69 : 26269783423012464
    double!(var1, 4);
    //   73 : 26269783423012479
    var1.add_assign(&var5);
    //   74 : 420316534768199664
    double!(var1, 4);
    //   78 : 420316534768199665
    var1.add_assign(var0);
    //   79 : 107601032900659114240
    double!(var1, 8);
    //   87 : 107601032900659114249
    var1.add_assign(&var11);
    //   88 : 27545864422568733247744
    double!(var1, 8);
    //   96 : 27545864422568733247773
    var1.add_assign(&var8);
    //   97 : 440733830761099731964368
    double!(var1, 4);
    //  101 : 440733830761099731964373
    var1.add_assign(&var2);
    //  102 : 225655721349683062765758976
    double!(var1, 9);
    //  111 : 225655721349683062765758991
    var1.add_assign(&var5);
    //  112 : 14441966166379716017008575424
    double!(var1, 6);
    //  118 : 14441966166379716017008575433
    var1.add_assign(&var11);
    //  119 : 57767864665518864068034301732
    double!(var1, 2);
    //  121 : 57767864665518864068034301733
    var1.add_assign(var0);
    //  122 : 29577146708745658402833562487296
    double!(var1, 9);
    //  131 : 29577146708745658402833562487325
    var1.add_assign(&var8);
    //  132 : 946468694679861068890673999594400
    double!(var1, 5);
    //  137 : 946468694679861068890673999594421
    var1.add_assign(&var13);
    //  138 : 15143499114877777102250783993510736
    double!(var1, 4);
    //  142 : 15143499114877777102250783993510737
    var1.add_assign(var0);
    //  143 : 31013886187269687505409605618709989376
    double!(var1, 11);
    //  154 : 31013886187269687505409605618709989387
    var1.add_assign(&var9);
    //  155 : 3969777431970520000692429519194878641536
    double!(var1, 7);
    //  162 : 3969777431970520000692429519194878641563
    var1.add_assign(&var12);
    //  163 : 508131511292226560088630978456944466120064
    double!(var1, 7);
    //  170 : 508131511292226560088630978456944466120081
    var1.add_assign(&var7);
    //  171 : 16260208361351249922836191310622222915842592
    double!(var1, 5);
    //  176 : 16260208361351249922836191310622222915842619
    var1.add_assign(&var12);
    //  177 : 520326667563239997530758121939911133306963808
    double!(var1, 5);
    //  182 : 520326667563239997530758121939911133306963833
    var1.add_assign(&var14);
    //  183 : 133203626896189439367874079216617250126582741248
    double!(var1, 8);
    //  191 : 133203626896189439367874079216617250126582741269
    var1.add_assign(&var13);
    //  192 : 8525032121356124119543941069863504008101295441216
    double!(var1, 6);
    //  198 : 8525032121356124119543941069863504008101295441223
    var1.add_assign(&var3);
    //  199 : 272801027883395971825406114235632128259241454119136
    double!(var1, 5);
    //  204 : 272801027883395971825406114235632128259241454119137
    var1.add_assign(var0);
    //  205 : 69837063138149368787303965244321824834365812254499072
    double!(var1, 8);
    //  213 : 69837063138149368787303965244321824834365812254499083
    var1.add_assign(&var9);
    //  214 : 4469572040841559602387453775636596789399411984287941312
    double!(var1, 6);
    //  220 : 4469572040841559602387453775636596789399411984287941333
    var1.add_assign(&var13);
    //  221 : 71513152653464953638199260410185548630390591748607061328
    double!(var1, 4);
    //  225 : 71513152653464953638199260410185548630390591748607061341
    var1.add_assign(&var10);
    //  226 : 1144210442455439258211188166562968778086249467977712981456
    double!(var1, 4);
    //  230 : 1144210442455439258211188166562968778086249467977712981461
    var1.add_assign(&var2);
    //  231 : 73229468317148112525516042660030001797519965950573630813504
    double!(var1, 6);
    //  237 : 73229468317148112525516042660030001797519965950573630813517
    var1.add_assign(&var10);
    //  238 : 4686685972297479201633026730241920115041277820836712372065088
    double!(var1, 6);
    //  244 : 4686685972297479201633026730241920115041277820836712372065093
    var1.add_assign(&var2);
    //  245 : 74986975556759667226128427683870721840660445133387397953041488
    double!(var1, 4);
    //  249 : 74986975556759667226128427683870721840660445133387397953041489
    var1.add_assign(var0);
    //  250 : 76786662970121899239555509948283619164836295816588695503914484736
    double!(var1, 10);
    //  260 : 76786662970121899239555509948283619164836295816588695503914484747
    var1.add_assign(&var9);
    //  261 : 4914346430087801551331552636690151626549522932261676512250527023808
    double!(var1, 6);
    //  267 : 4914346430087801551331552636690151626549522932261676512250527023833
    var1.add_assign(&var14);
    //  268 : 78629542881404824821304842187042426024792366916186824196008432381328
    double!(var1, 4);
    //  272 : 78629542881404824821304842187042426024792366916186824196008432381335
    var1.add_assign(&var3);
    //  273 : 5032290744409908788563509899970715265586711482635956748544539672405440
    double!(var1, 6);
    //  279 : 5032290744409908788563509899970715265586711482635956748544539672405451
    var1.add_assign(&var9);
    //  280 : 322066607642234162468064633598125776997549534888701231906850539033948864
    double!(var1, 6);
    //  286 : 322066607642234162468064633598125776997549534888701231906850539033948883
    var1.add_assign(&var15);
    //  287 : 10306131444551493198978068275140024863921585116438439421019217249086364256
    double!(var1, 5);
    //  292 : 10306131444551493198978068275140024863921585116438439421019217249086364285
    var1.add_assign(&var8);
    //  293 : 329796206225647782367298184804480795645490723726030061472614951970763657120
    double!(var1, 5);
    //  298 : 329796206225647782367298184804480795645490723726030061472614951970763657147
    var1.add_assign(&var12);
    //  299 : 5276739299610364517876770956871692730327851579616480983561839231532218514352
    double!(var1, 4);
    // 303 : 5276739299610364517876770956871692730327851579616480983561839231532218514367
    var1.add_assign(&var5);
    //  304 : 337711315175063329144113341239788334740982501095454782947957710818061984919488
    double!(var1, 6);
    //  310 : 337711315175063329144113341239788334740982501095454782947957710818061984919507
    var1.add_assign(&var15);
    //  311 : 21613524171204053065223253839346453423422880070109106108669293492355967034848448
    double!(var1, 6);
    //  317 : 21613524171204053065223253839346453423422880070109106108669293492355967034848453
    var1.add_assign(&var2);
    //  318 : 2766531093914118792348576491436346038198128648973965581909669567021563780460601984
    double!(var1, 7);
    //  325 : 2766531093914118792348576491436346038198128648973965581909669567021563780460601999
    var1.add_assign(&var5);
    //  326 : 177057990010503602710308895451926146444680233534333797242218852289380081949478527936
    double!(var1, 6);
    //  332 : 177057990010503602710308895451926146444680233534333797242218852289380081949478527943
    var1.add_assign(&var3);
    //  333 : 11331711360672230573459769308923273372459534946197363023502006546520325244766625788352
    double!(var1, 6);
    //  339 : 11331711360672230573459769308923273372459534946197363023502006546520325244766625788363
    var1.add_assign(&var9);
    //  340 : 725229527083022756701425235771089495837410236556631233504128418977300815665064050455232
    double!(var1, 6);
    //  346 : 725229527083022756701425235771089495837410236556631233504128418977300815665064050455251
    var1.add_assign(&var15);
    //  347 : 46414689733313456428891215089349727733594255139624398944264218814547252202564099229136064
    double!(var1, 6);
    //  353 : 46414689733313456428891215089349727733594255139624398944264218814547252202564099229136089
    var1.add_assign(&var14);
    //  354 : 1485270071466030605724518882859191287475016164467980766216455002065512070482051175332354848
    double!(var1, 5);
    //  359 : 1485270071466030605724518882859191287475016164467980766216455002065512070482051175332354877
    var1.add_assign(&var8);
    //  360 : 1520916553181215340261907336047811878374416552415212304605649922115084360173620403540331394048
    double!(var1, 10);
    //  370 : 1520916553181215340261907336047811878374416552415212304605649922115084360173620403540331394071
    var1.add_assign(&var6);
    //  371 : 48669329701798890888381034753529980107981329677286793747380797507682699525555852913290604610272
    double!(var1, 5);
    //  376 : 48669329701798890888381034753529980107981329677286793747380797507682699525555852913290604610287
    var1.add_assign(&var5);
    //  377 : 389354637614391127107048278028239840863850637418294349979046380061461596204446823306324836882296
    double!(var1, 3);
    //  380 : 389354637614391127107048278028239840863850637418294349979046380061461596204446823306324836882297
    var1.add_assign(var0);
    //  381 : 199349574458568257078808718350458798522291526358166707189271746591468337256676773532838316483736064
    double!(var1, 9);
    //  390 : 199349574458568257078808718350458798522291526358166707189271746591468337256676773532838316483736085
    var1.add_assign(&var13);
    //  391 : 25516745530696736906087515948858726210853315373845338520226783563707947168854627012203304509918218880
    double!(var1, 7);
    //  398 : 25516745530696736906087515948858726210853315373845338520226783563707947168854627012203304509918218907
    var1.add_assign(&var12);
    //  399 : 408267928491147790497400255181739619373653045981525416323628537019327154701674032195252872158691502512
    double!(var1, 4);
    //  403 : 408267928491147790497400255181739619373653045981525416323628537019327154701674032195252872158691502527
    var1.add_assign(&var5);
    //  404 : 26129147423433458591833616331631335639913794942817626644712226369236937900907138060496183818156256161728
    double!(var1, 6);
    //  410 : 26129147423433458591833616331631335639913794942817626644712226369236937900907138060496183818156256161733
    var1.add_assign(&var2);
    //  411 : 1672265435099741349877351445224405480954482876340328105261582487631164025658056835871755764362000394350912
    double!(var1, 6);
    //  417 : 1672265435099741349877351445224405480954482876340328105261582487631164025658056835871755764362000394350921
    var1.add_assign(&var11);
    //  418 : 26756246961595861598037623123590487695271726021445249684185319802098624410528909373948092229792006309614736
    double!(var1, 4);
    // 422 : 26756246961595861598037623123590487695271726021445249684185319802098624410528909373948092229792006309614749
    var1.add_assign(&var10);
    //  423 : 428099951385533785568601969977447803124347616343123994946965116833577990568462549983169475676672100953835984
    double!(var1, 4);
    //  427 : 428099951385533785568601969977447803124347616343123994946965116833577990568462549983169475676672100953835987
    var1.add_assign(&var4);
    //  428 : 27398396888674162276390526078556659399958247445959935676605767477348991396381603198922846443307014461045503168
    double!(var1, 6);
    //  434 : 27398396888674162276390526078556659399958247445959935676605767477348991396381603198922846443307014461045503181
    var1.add_assign(&var10);
    //  435 : 3506994801750292771377987338055252403194655673082871766605538237100670898736845209462124344743297851013824407168
    double!(var1, 7);
    //  442 : 3506994801750292771377987338055252403194655673082871766605538237100670898736845209462124344743297851013824407185
    var1.add_assign(&var7);
    //  443 : 28055958414002342171023898704442019225557245384662974132844305896805367189894761675696994757946382808110595257480
    double!(var1, 3);
    //  446 : 28055958414002342171023898704442019225557245384662974132844305896805367189894761675696994757946382808110595257485
    var1.add_assign(&var2);
    //  447 : 448895334624037474736382379271072307608915926154607586125508894348885875038316186811151916127142124929769524119760
    double!(var1, 4);
    //  451 : 448895334624037474736382379271072307608915926154607586125508894348885875038316186811151916127142124929769524119767
    var1.add_assign(&var3);
    //  452 : 114917205663753593532513889093394510747882477095579542048130276953314784009808943823654890528548383982020998174660352
    double!(var1, 8);
    //  460 : 114917205663753593532513889093394510747882477095579542048130276953314784009808943823654890528548383982020998174660363
    var1.add_assign(&var9);
    //  461 : 29418804649920919944323555607908994751457914136468362764321350900048584706511089618855651975308386299397375532713052928
    double!(var1, 8);
    //  469 : 29418804649920919944323555607908994751457914136468362764321350900048584706511089618855651975308386299397375532713052939
    var1.add_assign(&var9);
    //  470 : 1882803497594938876436707558906175664093306504733975216916566457603109421216709735606761726419736723161432034093635388096
    double!(var1, 6);
    //  476 : 1882803497594938876436707558906175664093306504733975216916566457603109421216709735606761726419736723161432034093635388125
    var1.add_assign(&var8);
    //  477 : 60249711923038044045974641884997621250985808151487206941330126643299501478934711539416375245431575141165825090996332420000
    double!(var1, 5);
    //  482 : 60249711923038044045974641884997621250985808151487206941330126643299501478934711539416375245431575141165825090996332420017
    var1.add_assign(&var7);
    //  483 : 1927990781537217409471188540319923880031545860847590622122564052585584047325910769261324007853810404517306402911882637440544
    double!(var1, 5);
    //  488 : 1927990781537217409471188540319923880031545860847590622122564052585584047325910769261324007853810404517306402911882637440567
    var1.add_assign(&var6);
    //  489 : 123391410018381914206156066580475128322018935094245799815844099365477379028858289232724736502643865889107609786360488796196288
    double!(var1, 6);
    //  495 : 123391410018381914206156066580475128322018935094245799815844099365477379028858289232724736502643865889107609786360488796196303
    var1.add_assign(&var5);
    //  496 : 7897050241176442509193988261150408212609211846031731188214022359390552257846930510894383136169207416902887026327071282956563392
    double!(var1, 6);
    //  502 : 7897050241176442509193988261150408212609211846031731188214022359390552257846930510894383136169207416902887026327071282956563395
    var1.add_assign(&var4);
    //  503 : 252705607717646160294207624356813062803494779073015398022848715500497672251101776348620260357414637340892384842466281054610028640
    double!(var1, 5);
    //  508 : 252705607717646160294207624356813062803494779073015398022848715500497672251101776348620260357414637340892384842466281054610028655
    var1.add_assign(&var5);
    //  509 : 16173158893929354258829287958836036019423665860672985473462317792031851024070513686311696662874536789817112629917841987495041833920
    double!(var1, 6);
    //  515 : 16173158893929354258829287958836036019423665860672985473462317792031851024070513686311696662874536789817112629917841987495041833923
    var1.add_assign(&var4);
    //  516 : 1035082169211478672565074429365506305243114615083071070301588338690038465540512875923948586423970354548295208314741887199682677371072
    double!(var1, 6);
    //  522 : 1035082169211478672565074429365506305243114615083071070301588338690038465540512875923948586423970354548295208314741887199682677371079
    var1.add_assign(&var3);
    //  523 : 66245258829534635044164763479392403535559335365316548499301653676162461794592824059132709531134102691090893332143480780779691351749056
    double!(var1, 6);
    //  529 : 66245258829534635044164763479392403535559335365316548499301653676162461794592824059132709531134102691090893332143480780779691351749059
    var1.add_assign(&var4);
    //  530 : 4239696565090216642826544862681113826275797463380259103955305835274397554853940739784493409992582572229817173257182769969900246511939776
    double!(var1, 6);
    //  536 : 4239696565090216642826544862681113826275797463380259103955305835274397554853940739784493409992582572229817173257182769969900246511939791
    var1.add_assign(&var5);
    //  537 : 271340580165773865140898871211591284881651037656336582653139573457561443510652207346207578239525284622708299088459697278073615776764146624
    double!(var1, 6);
    //  543 : 271340580165773865140898871211591284881651037656336582653139573457561443510652207346207578239525284622708299088459697278073615776764146631
    var1.add_assign(&var3);
    //  544 : 34731594261219054738035055515083684464851332820011082579601865402567864769363482540314570014659236431706662283322841251593422819425810768768
    double!(var1, 7);
    //  551 : 34731594261219054738035055515083684464851332820011082579601865402567864769363482540314570014659236431706662283322841251593422819425810768775
    var1.add_assign(&var3);
    //  552 : 2222822032718019503234243552965355805750485300480709285094519385764343345239262882580132480938191131629226386132661840101979060443251889201600
    double!(var1, 6);
    //  558 : 2222822032718019503234243552965355805750485300480709285094519385764343345239262882580132480938191131629226386132661840101979060443251889201607
    var1.add_assign(&var3);
    //  559 : 71130305046976624103495793694891385784015529615382697123024620344458987047656412242564239390022116212135244356245178883263329934184060454451424
    double!(var1, 5);
    //  564 : 71130305046976624103495793694891385784015529615382697123024620344458987047656412242564239390022116212135244356245178883263329934184060454451427
    var1.add_assign(&var4);
    //  565 : 4552339523006503942623730796473048690176993895384492615873575702045375171050010383524111320961415437576655638799691448528853115787779869084891328
    double!(var1, 6);
    //  571 : 4552339523006503942623730796473048690176993895384492615873575702045375171050010383524111320961415437576655638799691448528853115787779869084891335
    var1.add_assign(&var3);
    //  572 : 291349729472416252327918770974275116171327609304607527415908844930904010947200664545543124541530588004905960883180252705846599410417911621433045440
    double!(var1, 6);
    //  578 : 291349729472416252327918770974275116171327609304607527415908844930904010947200664545543124541530588004905960883180252705846599410417911621433045447
    var1.add_assign(&var3);
    //  579 : 2330797835779330018623350167794200929370620874436860219327270759447232087577605316364344996332244704039247687065442021646772795283343292971464363576
    double!(var1, 3);
    //  582 : 2330797835779330018623350167794200929370620874436860219327270759447232087577605316364344996332244704039247687065442021646772795283343292971464363577
    var1.add_assign(var0);
    //  583 : 149171061489877121191894410738828859479719735963959054036945328604622853604966740247318079765263661058511851972188289385393458898133970750173719268928
    double!(var1, 6);
    //  589 : 149171061489877121191894410738828859479719735963959054036945328604622853604966740247318079765263661058511851972188289385393458898133970750173719268935
    var1.add_assign(&var3);
    //  590 : 9546947935352135756281242287285047006702063101693379458364501030695862630717871375828357104976874307744758526220050520665181369480574128011118033211840
    double!(var1, 6);
    //  596 : 9546947935352135756281242287285047006702063101693379458364501030695862630717871375828357104976874307744758526220050520665181369480574128011118033211847
    var1.add_assign(&var3);
    //  597 : 305502333931268344200999753193121504214466019254188142667664032982267604182971884026507427359259977847832272839041616661285803823378372096355777062779104
    double!(var1, 5);
    //  602 : 305502333931268344200999753193121504214466019254188142667664032982267604182971884026507427359259977847832272839041616661285803823378372096355777062779109
    var1.add_assign(&var2);

    // compute 3 * h2
    var2 = *var1;
    var2.double(); // 2 * h2
    var2.add_assign(var1); // 3 * h2

    // compute (z**2 - 1) * 3 * h2
    chain_z(&mut var3, &var2); // 3 * z * h2
    chain_z(var1, &var3); // 3 * z**2 * h2
    var1.sub_assign(&var2);
}

/// Trait implementing cofactor clearing for projective coords.
pub trait ClearH: CurveProjective {
    /// Clear the cofactor in-place
    fn clear_h(&mut self);
}

impl ClearH for G1 {
    // h_eff = 1 - z, therefore
    // out = in * chain_z(in)
    fn clear_h(&mut self) {
        let pt_in = *self;
        chain_z(self, &pt_in);
        self.add_assign(&pt_in);
    }
}

impl ClearH for G2 {
    // This function emulates the Budroni-Pintore method given by
    // section 4.1, equation 12 of https://eprint.iacr.org/2017/419.pdf.
    //
    // To avoid any potential issue with the GLV patent (US 7110538),
    // we *do not* use the endomorphism. Instead, we emulate this method
    // with a scalar multiplication by h2_eff = h2 * (3 * z^2 - 3), where
    // h2 is the cofactor on E2 and z is the BLS parameter, 0xd201000000010000.
    // This is equivalent per [BP17] Section 4.1.
    fn clear_h(&mut self) {
        let pt_in = *self;
        chain_h2_eff(self, &pt_in);
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::bls12_381::{Fq, Fq2, FqRepr, FrRepr, G1, G2};

    use fff::PrimeField;
    use groupy::CurveProjective;
    use rand_core::SeedableRng;

    const SEED: [u8; 16] = [
        0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
        0xe5,
    ];

    #[test]
    fn test_clear_h() {
        let mut rng = rand_xorshift::XorShiftRng::from_seed(SEED);
        for _ in 0..32 {
            let mut input = G1::random(&mut rng);
            let mut result = input;
            result.clear_h();
            input.mul_assign(0xd201000000010001u64);
            assert_eq!(result, input);
        }
    }

    #[test]
    fn test_clear_h2() {
        let mut rng = rand_xorshift::XorShiftRng::from_seed(SEED);

        // kinda sorta test
        for _ in 0..32 {
            let mut input = G2::random(&mut rng);
            let mut result = input;
            result.clear_h();
            input.mul_assign(FrRepr([
                0xa40200040001ffffu64,
                0xb116900400069009u64,
                0x0000000000000002u64,
                0x0000000000000000u64,
            ]));
            assert_eq!(result, input);
        }

        // really for real test
        let c0 = Fq::from_repr(FqRepr([
            0x60bfbecc732ba610u64,
            0x6603a5f54c58db2fu64,
            0x5d8eb4297c4d8279u64,
            0xb1bbb083d0728d9du64,
            0x79e52f9d6301e7a9u64,
            0x0c9fb76d56733b44u64,
        ]))
        .unwrap();
        let c1 = Fq::from_repr(FqRepr([
            0x2058eebaac3db022u64,
            0xd8f94159af393618u64,
            0x4e041f53ff779974u64,
            0x03a5f678559fecdcu64,
            0xcdb85eca3da1f440u64,
            0x006d55d738a89daau64,
        ]))
        .unwrap();
        let xi = Fq2 { c0, c1 };
        let c0 = Fq::from_repr(FqRepr([
            0x938225a3e8d53daau64,
            0x30ea7f357aaa77dfu64,
            0x63587f338dc75610u64,
            0x7b35c727ac61e96bu64,
            0x1e003da1f3a124f4u64,
            0x087785cfcb421f1fu64,
        ]))
        .unwrap();
        let c1 = Fq::from_repr(FqRepr([
            0xa75d1b64c7f88282u64,
            0xdfe0c7eba1fe426eu64,
            0x19272d81b8edef80u64,
            0x9ab5ce196e06fe79u64,
            0x8a355846ccb413d1u64,
            0x0923471c6b752c75u64,
        ]))
        .unwrap();
        let yi = Fq2 { c0, c1 };
        let c0 = Fq::from_repr(FqRepr([
            0x55165d667c9f9812u64,
            0xac6431be755ad550u64,
            0x97c399a16cf5d66bu64,
            0xc4f2c5ff5e7563e7u64,
            0xc240476aa653e0b2u64,
            0x0f7f362adfa23764u64,
        ]))
        .unwrap();
        let c1 = Fq::from_repr(FqRepr([
            0xb6267378d94c97b8u64,
            0x01bc8e83b89eccffu64,
            0x125c9b39aba71843u64,
            0xc130ce1872e2f21au64,
            0xe981bb12aaf40da3u64,
            0x13c645cc354af99du64,
        ]))
        .unwrap();
        let zi = Fq2 { c0, c1 };
        let mut pi = G2 {
            x: xi,
            y: yi,
            z: zi,
        };
        pi.clear_h();
        let c0 = Fq::from_repr(FqRepr([
            0x2a31a2dd0fdb0639u64,
            0x56c20026fc05a72du64,
            0x803739ef1dfbb449u64,
            0x04fc1b828144bdf6u64,
            0xeaceed987948436du64,
            0x1470136456244901u64,
        ]))
        .unwrap();
        let c1 = Fq::from_repr(FqRepr([
            0xa659d96591a5b1ddu64,
            0xe0865f2fb7c23ef2u64,
            0x0ef5af32f3c9d18eu64,
            0x84bd02cb19fc81cfu64,
            0x6b4b92771dd8b717u64,
            0x0b55195ae0adcc28u64,
        ]))
        .unwrap();
        let xo = Fq2 { c0, c1 };
        let c0 = Fq::from_repr(FqRepr([
            0xe96d68bfe2d19c9eu64,
            0xc866562b27937ae3u64,
            0xfdf2fc54562635e0u64,
            0x912e94ab3c21d229u64,
            0xc11f34aefe94c01au64,
            0x17c43b238fba8709u64,
        ]))
        .unwrap();
        let c1 = Fq::from_repr(FqRepr([
            0xaaecf08cd1008aa4u64,
            0x6a2f4b8cd343c879u64,
            0x359faf89d61a09a1u64,
            0xa5b3631b436b673bu64,
            0xf8feb650d6b3f3e9u64,
            0x009b1ff5dfcde663u64,
        ]))
        .unwrap();
        let yo = Fq2 { c0, c1 };
        let c0 = Fq::from_repr(FqRepr([
            0xec7dce5a7896e240u64,
            0x938083998c2a5d40u64,
            0x39bf9d8500c9c8efu64,
            0xc0bb723e4646e48fu64,
            0xa33859cef4f3d803u64,
            0x16046ed5637f1cebu64,
        ]))
        .unwrap();
        let c1 = Fq::from_repr(FqRepr([
            0x7fd2dd34eb3df4dbu64,
            0x28ca7b0791108e03u64,
            0x67e02cd3f84a6f33u64,
            0x53e182e58667e803u64,
            0x4bc9e4801c0e6f45u64,
            0x11b7c228955190f9u64,
        ]))
        .unwrap();
        let zo = Fq2 { c0, c1 };
        let po = G2 {
            x: xo,
            y: yo,
            z: zo,
        };
        assert_eq!(pi, po);

        let c0 = Fq::from_repr(FqRepr([
            0x6fa65f65fa648214u64,
            0x2dd4f6998a8cbad5u64,
            0x279f4d81f93074e9u64,
            0x59771054bff8e5c9u64,
            0x301cacbeb813b681u64,
            0x0936c756f4e4ef7au64,
        ]))
        .unwrap();
        let c1 = Fq::from_repr(FqRepr([
            0x4dbcb567de5d656bu64,
            0x81115c11f506f4a2u64,
            0x9c85b49117e4cd56u64,
            0x9060f0e2b1a73fe1u64,
            0xc83a89a675fd5bf1u64,
            0x0e1d5f9cd7fbe4d8u64,
        ]))
        .unwrap();
        let xi = Fq2 { c0, c1 };
        let c0 = Fq::from_repr(FqRepr([
            0xb23800817a8e4504u64,
            0xf7c8d030606cf5d3u64,
            0xc554d5f3a6873b52u64,
            0xde3f28167d9a5291u64,
            0x4f4d918a1865778du64,
            0x132afbf2f8f65a1eu64,
        ]))
        .unwrap();
        let c1 = Fq::from_repr(FqRepr([
            0xcc9ef3087fed27afu64,
            0x7e81a2f64391f0beu64,
            0xc48938b12beb0fbfu64,
            0x360c79002c1e90f7u64,
            0x751da7c5a9e8babfu64,
            0x0ebd04f9163cec3du64,
        ]))
        .unwrap();
        let yi = Fq2 { c0, c1 };
        let c0 = Fq::from_repr(FqRepr([
            0xe3338c82ea2979b4u64,
            0x9a6f91d415db545bu64,
            0xa3ca77e0d9861d1cu64,
            0x28f2f4c58ddda9b9u64,
            0x4619fd312fda5b8au64,
            0x05cedc83f8d1ef6du64,
        ]))
        .unwrap();
        let c1 = Fq::from_repr(FqRepr([
            0x67b92685e8403d67u64,
            0x60023680e19a4a74u64,
            0x3f08353c8d07f724u64,
            0xd9e2e0af812f9dcfu64,
            0xd14586ab798fd681u64,
            0x0fd1302c1e7f0f46u64,
        ]))
        .unwrap();
        let zi = Fq2 { c0, c1 };
        let mut pi = G2 {
            x: xi,
            y: yi,
            z: zi,
        };
        pi.clear_h();
        let c0 = Fq::from_repr(FqRepr([
            0x4c8957d8d8815b9bu64,
            0x7eeeba08557e6adfu64,
            0x27ec4ebc182fb6eau64,
            0x3813d28668925384u64,
            0x168507538152ff6eu64,
            0x073f71e403e113e7u64,
        ]))
        .unwrap();
        let c1 = Fq::from_repr(FqRepr([
            0xb7166aeff1af65f1u64,
            0x0dfdd3aad2611503u64,
            0x66f71aea8543e538u64,
            0xad827b476a580daeu64,
            0xa01f125180bdfbafu64,
            0x128a5c629c0b95aeu64,
        ]))
        .unwrap();
        let xo = Fq2 { c0, c1 };
        let c0 = Fq::from_repr(FqRepr([
            0xc17fa5b0dc489902u64,
            0x0b388a0fc48ad69fu64,
            0x8175bd9a07bfca84u64,
            0x9fbfe48a85acba8du64,
            0x611f3be0a870feb3u64,
            0x04bb1864f86691dcu64,
        ]))
        .unwrap();
        let c1 = Fq::from_repr(FqRepr([
            0x05bcb86bb3bd9ac5u64,
            0xc12b98541bc9b825u64,
            0xe799456b05496e88u64,
            0xd3e521e467210692u64,
            0xbe800d10cbccee05u64,
            0x0de0e0750127f90fu64,
        ]))
        .unwrap();
        let yo = Fq2 { c0, c1 };
        let c0 = Fq::from_repr(FqRepr([
            0x0eba7a5361d94a4bu64,
            0x6ab1c7c60e71695cu64,
            0xc8bb6f7a7b3a28f0u64,
            0x796502f270c9af00u64,
            0x400ad08f5ce56103u64,
            0x0ebaf76abe831eb9u64,
        ]))
        .unwrap();
        let c1 = Fq::from_repr(FqRepr([
            0x674784654769a83fu64,
            0x3a8ca2cfe26e6c68u64,
            0x7231a53523ca451du64,
            0x3e31339b6cb09cb6u64,
            0xdfec96c2494da8c8u64,
            0x119759a94166869fu64,
        ]))
        .unwrap();
        let zo = Fq2 { c0, c1 };
        let po = G2 {
            x: xo,
            y: yo,
            z: zo,
        };
        assert_eq!(pi, po);
    }
}
